/*
  This exmple program provides a trivial server program that listens for TCP
  connections on port 9995.  When they arrive, it writes a short message to
  each client connection, and closes each connection once it is flushed.

  Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>

void listener_cb(evconnlistener *listener, evutil_socket_t fd,
         sockaddr *sock, int socklen, void *arg);

void socket_read_cb(bufferevent *bev, void *arg);

void socket_event_cb(bufferevent *bev, short events, void *arg);

static void signal_cb(evutil_socket_t, short, void *);


int main(int argc, char **argv) {

#ifdef WIN32
	WORD wVersion;
	WSADATA wsaData;
	wVersion = MAKEWORD(1, 1);
	int err = WSAStartup(wVersion, &wsaData);
	if (0 != err) {
		printf("error init.\n");
		return false;
	}

	if (wsaData.wVersion != wVersion) {
		printf("error init\n");
		WSACleanup();
		return false;
	}
#endif

    sockaddr_in sin;
    memset(&sin, 0, sizeof(sockaddr_in));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(25092);

    event_base *base = event_base_new();
    evconnlistener *listener
        = evconnlistener_new_bind(base,
                      listener_cb, base,
                      LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
                      10, (sockaddr*)&sin, sizeof(sockaddr_in));

	struct event *signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);

	if (!signal_event || event_add(signal_event, NULL)<0) {
		fprintf(stderr, "Could not create/add a signal event!\n");
		return 1;
	}

    printf("listening...\n");
    event_base_dispatch(base);

    evconnlistener_free(listener);
	event_free(signal_event);
    event_base_free(base);

#ifdef WIN32
    WSACleanup();
#endif

	printf("done!\n");

}

void listener_cb(evconnlistener *listener, evutil_socket_t fd,
        sockaddr *sock, int socklen, void *arg) {

    printf("accept a client %d\n", fd);
    event_base *base = (event_base *) arg;

    bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

    bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, sock);
    bufferevent_enable(bev, EV_READ | EV_PERSIST);

	char *msg = "connected\n";

	bufferevent_write(bev, msg, strlen(msg));
}


void socket_read_cb(bufferevent *bev, void *arg) {
	sockaddr *sock = (sockaddr*)arg;
    char msg[4096];
    size_t len = bufferevent_read(bev, msg, sizeof(msg) - 1);

	if (len <= 0) return;

    msg[len] = '\0';

    char reply[5000];
	
	sprintf(reply, "%s: %s", inet_ntoa(((sockaddr_in*)sock)->sin_addr), msg);

    printf("Server read the data from %s\n", reply);
    bufferevent_write(bev, msg, strlen(msg));
}

void socket_event_cb(bufferevent *bev, short events, void *arg) {
    if (events & BEV_EVENT_EOF) {
        printf("connection closed\n");
    }
    else if (events & BEV_EVENT_ERROR) {
        printf("some other error\n");
    }

    bufferevent_free(bev);
}

static void signal_cb(evutil_socket_t sig, short events, void *user_data) {
	struct event_base *base = (event_base*)user_data;
	struct timeval delay = { 2, 0 };

	printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

	event_base_loopexit(base, &delay);
}
